package org.codefilarete.tool.collection;

import java.util.Iterator;

/**
 * {@link Iterator} that invokes a method every N iterations
 * {@link #hasNext()} triggers the invocation to {@link #onStep()} to mimic a while(hasNext) or a foreach : {@link #next()} doesn't do it because
 * it seems to be one iteration too late in this context of usage.
 * {@link #onStep()} is also invoked when {@link #hasNext()} returns false (except if it is the very first call) to treat the remaining objects.
 * 
 * @author Guillaume Mary
 * @see #onStep()
 */
public abstract class SteppingIterator<E> implements Iterator<E> {
	
	private final Iterator<? extends E> delegate;
	private long stepCount = 0;
	private final long stepSize;
	
	public SteppingIterator(Iterable<? extends E> delegate, long stepSize) {
		this(delegate.iterator(), stepSize);
	}
	
	public SteppingIterator(Iterator<? extends E> delegate, long stepSize) {
		this.delegate = delegate;
		this.stepSize = stepSize;
	}
	
	@Override
	public boolean hasNext() {
		boolean hasNext = delegate.hasNext();
		if (	// step reached
				stepCount == stepSize
				// or has remainings (end reached and not started)
				|| (!hasNext && stepCount != 0)) {
			onStep();
			stepCount = 0;
		}
		return hasNext;
	}
	
	@Override
	public E next() {
		stepCount++;
		return delegate.next();
	}
	
	public long getStepSize() {
		return stepSize;
	}
	
	public long getStepCount() {
		return stepCount;
	}
	
	/**
	 * To be overridden to perform some action when steps are reached and remaining items are processed (on {@link #hasNext()} returns false)
	 */
	protected abstract void onStep();
	
	@Override
	public void remove() {
		delegate.remove();
	}
}
